/*
 * acooly.cn Inc.
 * Copyright (c) 2016 All Rights Reserved.
 * create by zhangpu
 * date:2016年4月4日
 *
 */
package com.acooly.module.openapi.client.provider.yibao.marshall;

import com.acooly.core.utils.Reflections;
import com.acooly.core.utils.Strings;
import com.acooly.core.utils.validate.Validators;
import com.acooly.module.openapi.client.api.exception.ApiClientException;
import com.acooly.module.openapi.client.api.message.MessageFactory;
import com.acooly.module.openapi.client.api.util.Jsons;
import com.acooly.module.openapi.client.provider.yibao.OpenAPIClientYibaoProperties;
import com.acooly.module.openapi.client.provider.yibao.YibaoConstants;
import com.acooly.module.openapi.client.provider.yibao.domain.*;
import com.acooly.module.openapi.client.provider.yibao.enums.YibaoApiServiceType;
import com.acooly.module.openapi.client.provider.yibao.exception.YibaoProcessingException;
import com.acooly.module.openapi.client.provider.yibao.utils.SignUtils;
import com.acooly.module.openapi.client.provider.yibao.utils.StringHelper;
import com.acooly.module.safety.Safes;
import com.acooly.module.safety.key.KeyLoadManager;
import com.acooly.module.safety.signature.SignTypeEnum;
import com.acooly.module.safety.support.KeyPair;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.parser.Feature;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.net.URLEncoder;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

/**
 * @author zhangpu
 */
@Slf4j
public class YibaoAbstractMarshall {

    @Autowired
    protected OpenAPIClientYibaoProperties openAPIClientFudianProperties;

    @Autowired
    protected KeyLoadManager keyStoreLoadManager;

    @Resource(name = "yibaoMessageFactory")
    private MessageFactory messageFactory;


    protected String doMarshall(YibaoRequest source) {
        try {
            source.setPartner(getProperties().getMerchantNo());
            settingNotifyUrl(source);
            doVerifyParam(source);
            TreeMap<String, String> signMap = getSignDataMap(source);
            String waitSignStr = SignUtils.getSignStr(signMap);
            log.info("待签字符串：{}", waitSignStr);
            String sign = doSign(waitSignStr);
            signMap.put(YibaoConstants.SIGN, sign);
            String encryptDataSrc = Jsons.toJson(signMap);
            log.info("请求报文明文：{}", encryptDataSrc);
            //加密生成业务参数原文
            String encryptData = SignUtils.aesEncode(getProperties().getAesKey(), encryptDataSrc);
            //加密AESKey
            String encryptKey = SignUtils.rsaEncode(getProperties().getAesKey(), getKeyPair().getPublicKey());
            Map<String, String> datas = Maps.newTreeMap();
            datas.put(YibaoConstants.MERCHANT_NO, getProperties().getMerchantNo());
            datas.put(YibaoConstants.ENCRYPT_KEY, URLEncoder.encode(encryptKey, "UTF-8"));
            datas.put(YibaoConstants.ENCRYPT_DATA, URLEncoder.encode(encryptData, "UTF-8"));
            String message = StringHelper.getRequestStr(datas);
            return message;
        }catch (Exception e) {
            log.info("组装请求报文异常：{}",e.getMessage());
            throw  new ApiClientException("组装请求报文异常："+e.getMessage());
        }
    }


    protected YibaoResponse doUnMarshall(String message, String serviceName, boolean notify) {
        try {
            log.info("响应报文密文:{}", message);
            JSONObject jsonObject = JSONObject.parseObject(message, Feature.OrderedField);
            String errorCode = jsonObject.getString(YibaoConstants.ERROR_CODE);
            String respMessage = "";
            if(Strings.isBlank(errorCode)) {
                String data = jsonObject.getString(YibaoConstants.ENCRYPT_DATA);
                String encryptkey = jsonObject.getString(YibaoConstants.ENCRYPT_KEY);
                String ybAesKey = SignUtils.rasDecrypt(encryptkey, getKeyPair().getPrivateKey());
                respMessage = SignUtils.aesDecode(ybAesKey,data);
                log.info("响应报文明文：{}",respMessage);
                TreeMap<String, String> respMap = Jsons.parse(respMessage, TreeMap.class);
                String sign = respMap.get(YibaoConstants.SIGN);
                String verifySignStr = SignUtils.getVerifySignStr(respMap);
                //验签
                doVerify(verifySignStr, sign);
            } else {
                respMessage = message;
            }
            YibaoResponse yibaoResponse = null;
            if (notify) {
                yibaoResponse = (YibaoResponse) messageFactory.getNotify(serviceName);
            } else {
                yibaoResponse = (YibaoResponse) messageFactory.getResponse(serviceName);
            }
            yibaoResponse = Jsons.parse(respMessage, yibaoResponse.getClass());
            return yibaoResponse;
        } catch (Exception e) {
            throw new YibaoProcessingException("解析报文错误:" + e.getMessage());
        }
    }

    public void settingNotifyUrl(YibaoRequest apiMessage) {
        YibaoApiMsg yibaoApiMsg = YibaoConstants.getApiMsgInfo(apiMessage);
        apiMessage.setService(yibaoApiMsg.service().getCode());
        if (Strings.isBlank(apiMessage.getNotifyUrl()) && yibaoApiMsg.service().getApiServiceType() != YibaoApiServiceType.SYNC) {
            apiMessage.setNotifyUrl(YibaoConstants.getCanonicalUrl(openAPIClientFudianProperties.getDomain(),
                    openAPIClientFudianProperties.getNotifyUrl()
                            + yibaoApiMsg.service().code()));
        }
        if (yibaoApiMsg.service().getApiServiceType() == YibaoApiServiceType.REDIRECT && Strings.isBlank(apiMessage.getReturnUrl())) {
            throw new RuntimeException("跳转接口returnUrl是必须的");
        }
    }

    /**
     * 获取代签map
     *
     * @param source
     * @return
     */
    protected TreeMap<String, String> getSignDataMap(YibaoRequest source) {
        TreeMap<String, String> signData = Maps.newTreeMap();
        Set<Field> fields = Reflections.getFields(source.getClass());
        String key = null;
        Object value = null;
        for (Field field : fields) {
            value = Reflections.getFieldValue(source, field.getName());
            if (value == null) {
                continue;
            }
            YibaoAlias baofuAlias = field.getAnnotation(YibaoAlias.class);
            if (baofuAlias == null) {
                continue;
            } else {
                key = baofuAlias.value();
            }
            if (Strings.isNotBlank((String) value)) {
                signData.put(key, (String) value);
            }
        }
        return signData;
    }


    protected OpenAPIClientYibaoProperties getProperties() {
        return openAPIClientFudianProperties;
    }


    protected void doVerifyParam(YibaoMessage source) {
        try {
            source.doCheck();
            Validators.assertJSR303(source);
        } catch (Exception e) {
            throw new ApiClientException(e.getMessage());
        }
    }

    protected String doSign(String waitForSign) {
        return Safes.getSigner(SignTypeEnum.Rsa).sign(waitForSign, getKeyPair());
    }

    protected void doVerify(String plain, String sign) {
        try {
            Safes.getSigner(SignTypeEnum.Rsa).verify(plain, getKeyPair(), sign);
            log.info("验签成功");
        }catch (Exception e) {
            throw new ApiClientException("验签失败："+e.getMessage());
        }
    }

    protected KeyPair getKeyPair() {
        return keyStoreLoadManager.load(YibaoConstants.PROVIDER_DEF_PRINCIPAL, YibaoConstants.PROVIDER_NAME);
    }


}
